/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Description: Hmfs-tools resize utils
 */

#include "resize_utils.h"

#include <tuple>

namespace OHOS {
namespace Hmfs {
uint32_t CurrentSitAddr(std::shared_ptr<HmfsSuperBlockInfo> &superBlockInfo, uint32_t sitNumber)
{
    SegmentInfoTable *smInfo = superBlockInfo->segmentTable;
    SitInfo *sitInfo = smInfo->sitInfo;
    uint32_t offset = SIT_BLOCK_OFFSET(sitNumber);
    uint32_t blockAddr = sitInfo->sitBaseAddr + offset;
    CheckSegRange(superBlockInfo, sitNumber);
    if (HmfsCommon::GetInstance().VerifyBit(offset, sitInfo->sitBitmap)) {
        blockAddr += sitInfo->sitBlocks;
    }
    return blockAddr;
}

void ResetCurSegment(std::shared_ptr<HmfsSuperBlockInfo> &superBlockInfo, int32_t type)
{
    CurrentSegmentInfo *currentSegment = static_cast<CurrentSegmentInfo *>(
        superBlockInfo->segmentTable->currentSegmentArray + type);

    SummaryFooter *sumFooter = &(currentSegment->summaryBlockData->footer);
    if (memset_s(sumFooter, sizeof(SummaryFooter), 0, sizeof(SummaryFooter)) != 0) {
        return;
    }
    if (IsDataSegement(type)) {
        SET_SUM_TYPE(sumFooter, SUM_TYPE_DATA);
    }
    if (IsNodeSegement(type)) {
        SET_SUM_TYPE(sumFooter, SUM_TYPE_NODE);
    }
    SegmentEntry *sitEntry = GetSegEntry(superBlockInfo, currentSegment->segmentNumber);
    sitEntry->type = type;
    sitEntry->dirty = 1;
}

void UpdateSuperBlock(SuperBlockData *superBlock, int32_t superBlockMask)
{
    HMFS_DEBUG("Enter UpdateSuperBlock");
    if (superBlock == nullptr) {
        return;
    }
    uint8_t *buf = static_cast<uint8_t *>(calloc(BLOCK_SZ, 1));
    HMFS_ASSERT(buf);
    if (GetLeValue(superBlock->features) & HMFS_FEATURE_SB_CHKSUM) {
        uint32_t oldCrc = GetLeValue(superBlock->checksum);
        uint32_t newCrc = HmfsCommon::GetInstance().CalculateCrc32(
            HMFS_MAGIC_NUMBER, superBlock, SB_CHECKSUM_OFFSET);
        SetLeValue(superBlock->checksum, newCrc);
        HMFS_INFO("SueprBlock CRC is updated (0x%x -> 0x%x)\n", oldCrc, newCrc);
    }

    int32_t ret = memcpy_s(buf + HMFS_SUPER_BLOCK_OFFSET, sizeof(*superBlock), superBlock, sizeof(*superBlock));
    HMFS_CHECK_ONLY_LOG(ret == 0, "Failed to copy superblock");
    for (int32_t addr = static_cast<int32_t>(SuperBlockAddr::SUPER_BLOCK_0_ADDR);
        addr < static_cast<int32_t>(SuperBlockAddr::SUPER_BLOCK_MAX_ADDR); addr++) {
        if ((1 << addr) & superBlockMask) {
            ret = HmfsIo::GetInstance().DevWriteBlock(buf, addr);
            HMFS_ASSERT(ret >= 0);
        }
    }
    free(buf);
    HMFS_INFO("Done to update superblock\n");
}

void PrintRawSuperBlockInfo(SuperBlockData *superBlock, int32_t debugLevel, int32_t layout)
{
    if (debugLevel == 0 || superBlock == nullptr) {
        return;
    }
    if (layout) {
        printf("\n");
        printf("+--------------------------------------------------------+\n");
        printf("| Super block                                            |\n");
        printf("+--------------------------------------------------------+\n");
    }
    PRINT_TO_COLSOLE(superBlock, magicNumber);
    PRINT_TO_COLSOLE(superBlock, majorVersion);
    PRINT_TO_COLSOLE(superBlock, minorVersion);
    PRINT_TO_COLSOLE(superBlock, logSectorSize);
    PRINT_TO_COLSOLE(superBlock, log2SectorsPerBlock);

    PRINT_TO_COLSOLE(superBlock, logBlockSize);
    PRINT_TO_COLSOLE(superBlock, logBlksPerSeg);
    PRINT_TO_COLSOLE(superBlock, segsPerSection);
    PRINT_TO_COLSOLE(superBlock, sectionsPerZone);
    PRINT_TO_COLSOLE(superBlock, checksumOffset);
    PRINT_TO_COLSOLE(superBlock, blockCount);

    PRINT_TO_COLSOLE(superBlock, sectionCount);
    PRINT_TO_COLSOLE(superBlock, segmentCount);
    PRINT_TO_COLSOLE(superBlock, segmentCountInCP);
    PRINT_TO_COLSOLE(superBlock, segmentCountInSIT);
    PRINT_TO_COLSOLE(superBlock, segmentCountInNAT);

    PRINT_TO_COLSOLE(superBlock, segmentCountInSSA);
    PRINT_TO_COLSOLE(superBlock, segmentCountInMain);
    PRINT_TO_COLSOLE(superBlock, segment0BlockId);

    PRINT_TO_COLSOLE(superBlock, checkPointBlockId);
    PRINT_TO_COLSOLE(superBlock, sitBlockId);
    PRINT_TO_COLSOLE(superBlock, natBlockId);
    PRINT_TO_COLSOLE(superBlock, ssaBlockId);
    PRINT_TO_COLSOLE(superBlock, mainBlockId);

    PRINT_TO_COLSOLE(superBlock, rootInodeId);
    PRINT_TO_COLSOLE(superBlock, nodeInodeId);
    PRINT_TO_COLSOLE(superBlock, metaInodeId);
    PRINT_TO_COLSOLE(superBlock, checkPointPayload);
    PRINT_TO_COLSOLE(superBlock, checksum);
    printf("%-25s%-.255s", "version", superBlock->version);
    printf("\n");
}

void PrintCheckpointInfo(CheckPointData *checkPoint, int32_t debugLevel, int32_t layout)
{
    if (debugLevel == 0 || checkPoint == nullptr) {
        return;
    }
    if (layout) {
        printf("\n");
        printf("+--------------------------------------------------------+\n");
        printf("| Checkpoint                                             |\n");
        printf("+--------------------------------------------------------+\n");
    }
    constexpr uint32_t nodeIndex = 2;
    PRINT_TO_COLSOLE(checkPoint, checkPointVersion);
    PRINT_TO_COLSOLE(checkPoint, userBlockCount);
    PRINT_TO_COLSOLE(checkPoint, validBlockCount);
    PRINT_TO_COLSOLE(checkPoint, reservedSegmentCount);
    PRINT_TO_COLSOLE(checkPoint, overprovisionSegmentCount);
    PRINT_TO_COLSOLE(checkPoint, freeSegmentCount);

    PRINT_TO_COLSOLE(checkPoint, allocType[CURSEG_NODE_HOT]);
    PRINT_TO_COLSOLE(checkPoint, allocType[CURSEG_NODE_WARM]);
    PRINT_TO_COLSOLE(checkPoint, allocType[CURSEG_NODE_COLD]);
    PRINT_TO_COLSOLE(checkPoint, curNodeSegNo[0]);
    PRINT_TO_COLSOLE(checkPoint, curNodeSegNo[1]);
    PRINT_TO_COLSOLE(checkPoint, curNodeSegNo[nodeIndex]);

    PRINT_TO_COLSOLE(checkPoint, curNodeBlkOffset[0]);
    PRINT_TO_COLSOLE(checkPoint, curNodeBlkOffset[1]);
    PRINT_TO_COLSOLE(checkPoint, curNodeBlkOffset[nodeIndex]);

    PRINT_TO_COLSOLE(checkPoint, allocType[CURSEG_DATA_HOT]);
    PRINT_TO_COLSOLE(checkPoint, allocType[CURSEG_DATA_WARM]);
    PRINT_TO_COLSOLE(checkPoint, allocType[CURSEG_DATA_COLD]);
    PRINT_TO_COLSOLE(checkPoint, curDataSegNo[0]);
    PRINT_TO_COLSOLE(checkPoint, curDataSegNo[1]);
    PRINT_TO_COLSOLE(checkPoint, curDataSegNo[nodeIndex]);

    PRINT_TO_COLSOLE(checkPoint, curDataBlkOffset[0]);
    PRINT_TO_COLSOLE(checkPoint, curDataBlkOffset[1]);
    PRINT_TO_COLSOLE(checkPoint, curDataBlkOffset[nodeIndex]);

    PRINT_TO_COLSOLE(checkPoint, checkPointFlags);
    PRINT_TO_COLSOLE(checkPoint, checkPointPackBlockCount);
    PRINT_TO_COLSOLE(checkPoint, summaryStartBlock);
    PRINT_TO_COLSOLE(checkPoint, validNodeCount);
    PRINT_TO_COLSOLE(checkPoint, validInodeCount);
    PRINT_TO_COLSOLE(checkPoint, nextFreeNodeId);
    PRINT_TO_COLSOLE(checkPoint, sitVersionBitmapSize);
    PRINT_TO_COLSOLE(checkPoint, natVersionBitmapSize);
    PRINT_TO_COLSOLE(checkPoint, checksumOffset);
    PRINT_TO_COLSOLE(checkPoint, mountElapsedTime);

    PRINT_TO_COLSOLE(checkPoint, sitNatVersionBitmap[0]);
    printf("\n\n");
}

void PrintSuperBlockDataFeature(SuperBlockData *superBlockData)
{
    if (superBlockData == nullptr) {
        return;
    }
    uint32_t features = superBlockData->features;
    HMFS_DEBUG("superblock features = %x : ", features);

    std::tuple<uint32_t, const char*> featureList[] = {
        {NativeToLE32(HMFS_FEATURE_ENCRYPT), "encrypt"},
        {NativeToLE32(HMFS_FEATURE_VERITY), "verity"},
        {NativeToLE32(HMFS_FEATURE_BLKZONED), "blkzoned"},
        {NativeToLE32(HMFS_FEATURE_EXTRA_ATTR), "extra_attr"},
        {NativeToLE32(HMFS_FEATURE_PRJQUOTA), "project_quota"},
        {NativeToLE32(HMFS_FEATURE_INODE_CHKSUM), "inode_checksum"},
        {NativeToLE32(HMFS_FEATURE_FLEXIBLE_INLINE_XATTR), "flexible_inline_xattr"},
        {NativeToLE32(HMFS_FEATURE_QUOTA_INO), "quota_ino"},
        {NativeToLE32(HMFS_FEATURE_INODE_CRTIME), "inode_crtime"},
        {NativeToLE32(HMFS_FEATURE_LOST_FOUND), "lost_found"},
        {NativeToLE32(HMFS_FEATURE_SB_CHKSUM), "sb_checksum"},
        {NativeToLE32(HMFS_FEATURE_CASEFOLD), "casefold"},
        {NativeToLE32(HMFS_FEATURE_COMPRESSION), "compression"},
        {NativeToLE32(HMFS_FEATURE_RO), "ro"}
    };

    for (const auto &feature : featureList) {
        if (features & std::get<0>(feature)) {
            HMFS_DEBUG("%s", std::get<1>(feature));
        }
    }

    HMFS_DEBUG("superblock encrypt level = %d, salt = ", superBlockData->encryptionLevel);
    constexpr int32_t encryptionSaltSize = 16;
    for (int32_t i = 0; i < encryptionSaltSize; i++) {
        HMFS_DEBUG("%02x", superBlockData->encryptionSalt[i]);
    }
}

void PrintSuperBlockDataStopReason(SuperBlockData *superBlockData, int32_t forceStop)
{
    if (forceStop == 0 || superBlockData == nullptr) {
        return;
    }
    uint8_t *reason = superBlockData->stopReason;
    HMFS_DEBUG("checkpoint stop reason: ");
    std::string stopReasonStr[] = {"shutdown", "fault_inject", "meta_page",
        "write_fail", "corrupted_summary", "update_inode", "flush_fail", };
    for (uint32_t i = 0; i < CP_STOP_REASON_MAX; i++) {
        if (reason[i]) {
            HMFS_DEBUG("%s(%d) ", stopReasonStr[i].c_str(), reason[i]);
        }
    }
}

void PrintSuperBlockDataErrors(SuperBlockData *superBlockData, int32_t hmfsErrors)
{
    if (hmfsErrors == 0 || superBlockData == nullptr) {
        return;
    }
    std::string errorsStr[] = {"corrupted_cluster", "fail_decompression", "invalid_blkaddr",
        "corrupted_dirent", "corrupted_inode", "inconsistent_summary", "inconsistent_footer",
        "inconsistent_sum_type", "corrupted_journal", "inconsistent_node_count", "inconsistent_block_count",
        "invalid_curseg", "inconsistent_sit", "corrupted_verity_xattr", "corrupted_xattr", };
    uint8_t *errors = superBlockData->errors;
    HMFS_DEBUG("hmfs errors: ");
    for (int32_t i = 0; i < sizeof(errorsStr) / sizeof(errorsStr[0]); i++) {
        if (HmfsCommon::GetInstance().TestBitLe(i, errors)) {
            HMFS_DEBUG("%s ", errorsStr[i].c_str());
        }
    }
}

bool IsUsableSegment(std::shared_ptr<HmfsSuperBlockInfo> &superBlockInfo,
    uint32_t sitNumber, std::shared_ptr<HmfsConfigData> &config)
{
#ifdef HAVE_LINUX_BLKZONED_H
    uint32_t secno = sitNumber / superBlockInfo->segmentsPerSection;
    uint32_t segStart = START_BLOCK(superBlockInfo, sitNumber);
    uint32_t blocksPerSec = superBlockInfo->blocksPerSeg * superBlockInfo->segmentsPerSection;
    uint32_t devIdx = GetDeviceIdx(superBlockInfo, sitNumber, config);
    uint32_t zoneIdx = GetZoneIdxFromDev(superBlockInfo, sitNumber, devIdx, config);
    uint32_t secOff = superBlockInfo->segmentTable->mainBlockAddr >>
        HmfsCommon::GetInstance().LogBase2(blocksPerSec);

    if (zoneIdx < config->devices[devIdx].randomZonesCount) {
        return true;
    }
    if (config->devices[devIdx].zonedModel != HMFS_ZONED_HM) {
        return true;
    }
    return segStart < ((secOff + secno) * blocksPerSec) + config->devices[devIdx].zoneCapBlocks[zoneIdx];
#else
    return true;
#endif
}
} // namespace Hmfs
} // namespace OHOS